package com.yayawan.laomigame.callback;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;

import androidx.annotation.RequiresApi;

import com.tencent.smtt.sdk.ValueCallback;
import com.tencent.smtt.sdk.WebSettings;
import com.tencent.smtt.sdk.WebView;
import com.tencent.smtt.sdk.WebViewClient;
import com.ycbjie.webviewlib.base.RequestInfo;
import com.ycbjie.webviewlib.bridge.BridgeUtil;
import com.ycbjie.webviewlib.bridge.DefaultHandler;
import com.ycbjie.webviewlib.bridge.WebJsMessage;
import com.ycbjie.webviewlib.inter.BridgeHandler;
import com.ycbjie.webviewlib.inter.CallBackFunction;
import com.ycbjie.webviewlib.inter.WebViewJavascriptBridge;
import com.ycbjie.webviewlib.utils.X5LogUtils;
import com.ycbjie.webviewlib.view.BridgeWebView;

import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static android.os.Build.VERSION_CODES.KITKAT;


public class X5WebView extends WebView implements WebViewJavascriptBridge {
	TextView title;
	private WebViewClient client = new WebViewClient ()  {
		/**
		 * 防止加载网页时调起系统浏览器
		 */
		public boolean shouldOverrideUrlLoading(WebView view, String url) {
			view.loadUrl(url);
			return true;
		}
	};

	@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)




	private void initWebViewSettings() {
		WebSettings webSetting = this.getSettings();
		webSetting.setJavaScriptEnabled(true);
		webSetting.setJavaScriptCanOpenWindowsAutomatically(true);
		webSetting.setAllowFileAccess(true);
		webSetting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
		webSetting.setSupportZoom(true);
		webSetting.setBuiltInZoomControls(false);
		webSetting.setUseWideViewPort(true);
		webSetting.setSupportMultipleWindows(true);
		// webSetting.setLoadWithOverviewMode(true);
		webSetting.setAppCacheEnabled(true);
		// webSetting.setDatabaseEnabled(true);
		webSetting.setDomStorageEnabled(true);
		webSetting.setGeolocationEnabled(true);
		webSetting.setAppCacheMaxSize(Long.MAX_VALUE);
		// webSetting.setPageCacheCapacity(IX5WebSettings.DEFAULT_CACHE_CAPACITY);
		webSetting.setPluginState(WebSettings.PluginState.ON_DEMAND);
		// webSetting.setRenderPriority(WebSettings.RenderPriority.HIGH);
		webSetting.setCacheMode(WebSettings.LOAD_DEFAULT);
		webSetting.setMediaPlaybackRequiresUserGesture(false);
		settarget21();


		WebSettings ws = this.getSettings();
		// 网页内容的宽度是否可大于WebView控件的宽度
		//ws.setLoadWithOverviewMode(false);
		// 保存表单数据
		//ws.setSaveFormData(true);
		// 是否应该支持使用其屏幕缩放控件和手势缩放
		//ws.setSupportZoom(true);
		// 设置内置的缩放控件。若为false，则该WebView不可缩放
	//	ws.setBuiltInZoomControls(true);
		// 隐藏原生的缩放控件
	//	ws.setDisplayZoomControls(false);
		// 启动应用缓存
	//	ws.setAppCacheEnabled(true);
		// 设置缓存模式
		// 缓存模式如下：
		// LOAD_CACHE_ONLY: 不使用网络，只读取本地缓存数据
		// LOAD_DEFAULT: （默认）根据cache-control决定是否从网络上取数据。
		// LOAD_NO_CACHE: 不使用缓存，只从网络获取数据.
		// LOAD_CACHE_ELSE_NETWORK，只要本地有，无论是否过期，或者no-cache，都使用缓存中的数据。
		ws.setCacheMode(WebSettings.LOAD_DEFAULT);
		ws.setAppCacheMaxSize(Long.MAX_VALUE);
		// setDefaultZoom  api19被弃用
		// 设置此属性，可任意比例缩放。
	//	ws.setUseWideViewPort(true);
		// 告诉WebView启用JavaScript执行。默认的是false。
		// 注意：这个很重要   如果访问的页面中要与Javascript交互，则webview必须设置支持Javascript
	//	ws.setJavaScriptEnabled(true);
		//  页面加载好以后，再放开图片
		//ws.setBlockNetworkImage(false);
		// 使用localStorage则必须打开
	//	ws.setDomStorageEnabled(true);
		//防止中文乱码
	//	ws.setDefaultTextEncodingName("UTF-8");
		/*
		 * 排版适应屏幕
		 * 用WebView显示图片，可使用这个参数
		 * 设置网页布局类型： 1、LayoutAlgorithm.NARROW_COLUMNS ：
		 * 适应内容大小 2、LayoutAlgorithm.SINGLE_COLUMN:适应屏幕，内容将自动缩放
		 */
	//	ws.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
		// WebView是否新窗口打开(加了后可能打不开网页)
		//ws.setSupportMultipleWindows(true);
		// webview从5.0开始默认不允许混合模式,https中不能加载http资源,需要设置开启。
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
			ws.setMixedContentMode(android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
		}
		//设置字体默认缩放大小
		ws.setTextZoom(100);
		// 不缩放
		this.setInitialScale(100);
		if(Build.VERSION.SDK_INT >= KITKAT) {
			//设置网页在加载的时候暂时不加载图片
			ws.setLoadsImagesAutomatically(true);
		} else {
			ws.setLoadsImagesAutomatically(false);
		}
		//默认关闭硬件加速
		//setOpenLayerType(false);
		//默认不开启密码保存功能
		//setSavePassword(false);
		//移除高危风险js监听
		//setRemoveJavascriptInterface();
		// this.getSettingsExtension().setPageCacheCapacity(IX5WebSettings.DEFAULT_CACHE_CAPACITY);//extension
		// settings 的设计
	}

	@TargetApi(Build.VERSION_CODES.KITKAT)
	public void settarget21(){
        this.setWebContentsDebuggingEnabled(true);
    }

	@Override
	protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
		boolean ret = super.drawChild(canvas, child, drawingTime);
		
		return ret;
	}



	@Override
	public void send(String data) {

	}

	@Override
	public void send(String data, CallBackFunction responseCallback) {

	}

	public static final String TO_LOAD_JS = "WebViewJavascriptBridge.js";
	private long uniqueId = 0;
	private Map<String, CallBackFunction> responseCallbacks = new HashMap<>();
	private Map<String, BridgeHandler> messageHandlers = new HashMap<>();
	BridgeHandler defaultHandler = new DefaultHandler();
	private List<WebJsMessage> startupMessage = new ArrayList<>();
	/**
	 * loadUrl方法在19以上超过2097152个字符失效
	 */
	private static final int URL_MAX_CHARACTER_NUM = 2097152;
	private static final int EXEC_SCRIPT = 1;
	private static final int LOAD_URL = 2;
	private static final int LOAD_URL_WITH_HEADERS = 3;
	private static final int HANDLE_MESSAGE = 4;

	@SuppressLint("HandlerLeak")
	private Handler handler = new Handler(){
		@Override
		public void handleMessage(android.os.Message msg) {
			super.handleMessage(msg);
			switch (msg.what){
				case EXEC_SCRIPT:
					String javascriptCommand = (String) msg.obj;
					evaluateJavascriptUrl(javascriptCommand);
					break;
				case LOAD_URL:
					String url = (String) msg.obj;
					loadUrl(url);
					break;
				case LOAD_URL_WITH_HEADERS:
					RequestInfo info = (RequestInfo) msg.obj;
					loadUrl(info.url, info.headers);
					break;
				default:
					break;
			}
		}
	};

	/**
	 * 获取消息list集合
	 * @return							集合
	 */
	public List<WebJsMessage> getStartupMessage() {
		return startupMessage;
	}

	/**
	 * 设置消息，注意目前在onProgressChanged方法中调用
	 */
	public void setStartupMessage(List<WebJsMessage> startupMessage) {
		this.startupMessage = startupMessage;
	}

	public X5WebView(Context context, AttributeSet attrs) {
		super(context, attrs);
		initWebViewSettings();
		this.getRootView().setClickable(true);
		init();
	}

	public X5WebView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init();
	}

	public X5WebView(Context context) {
		super(context);
		init();
	}

	/**
	 * 开发者调用
	 * 主线程发送消息
	 * @param url						url
	 */
	@Override
	public void loadUrl(String url) {
		try {
			super.loadUrl(url);
			//Message msg = handler.obtainMessage(LOAD_URL, url);
			//handler.sendMessage(msg);
		}catch (Exception e){
			e.printStackTrace();
		}
	}

	/**
	 * 开发者调用
	 * 主线程发送消息
	 * @param url						url
	 */
	@Override
	public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
		Message msg = handler.obtainMessage(LOAD_URL_WITH_HEADERS, new RequestInfo(url, additionalHttpHeaders));
		handler.sendMessage(msg);
	}

	/**
	 * 开发者调用
	 * 子线程发送消息
	 * @param url						url
	 */
	public void postUrl(String url){
		if (Thread.currentThread() == Looper.getMainLooper().getThread()){
			loadUrl(url);
		} else {
			Message message = handler.obtainMessage();
			message.what = LOAD_URL;
			message.obj = url;
			handler.sendMessage(message);
		}
	}

	/**
	 * 开发者调用
	 * Android调用js方法
	 * @param script					方法
	 */
	public void evaluateJavascript(final String script) {
		if (Looper.getMainLooper() == Looper.myLooper()) {
			evaluateJavascriptUrl(script);
		} else {
			Message msg = handler.obtainMessage(EXEC_SCRIPT, script);
			handler.sendMessage(msg);
		}
	}

	/**
	 * Android调用js方法
	 * @param script					方法
	 */
	private void evaluateJavascriptUrl(String script) {
		X5LogUtils.d("分发message--------------"+script);
		//this.loadUrl(javascriptCommand);
		//开始执行js中_handleMessageFromNative方法
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
			evaluateJavascript(script, new ValueCallback<String>(){
				@Override
				public void onReceiveValue(String s) {
					X5LogUtils.i("---evaluateJavascript-1--"+s);
				}
			});
		} else {
			if (script.length()>=URL_MAX_CHARACTER_NUM){
				evaluateJavascript(script, new ValueCallback<String>(){
					@Override
					public void onReceiveValue(String s) {
						X5LogUtils.i("---evaluateJavascript-2--"+s);
					}
				});
			} else {
				loadUrl(script);
			}
		}
	}

	/**
	 * 默认处理程序，处理js发送的没有指定处理程序名称的消息，
	 * 如果js消息有处理程序名，它将由本机注册的命名处理程序处理
	 * @param handler							handler
	 */
	public void setDefaultHandler(BridgeHandler handler) {
		this.defaultHandler = handler;
	}

	private void init() {
		this.setVerticalScrollBarEnabled(false);
		this.setHorizontalScrollBarEnabled(false);
		this.getSettings().setJavaScriptEnabled(true);
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
			WebView.setWebContentsDebuggingEnabled(true);
		}
	}

	/**
	 * 获取到CallBackFunction data执行调用并且从数据集移除
	 * @param url						url链接
	 */
	public void handlerReturnData(String url) {
		String functionName = BridgeUtil.getFunctionFromReturnUrl(url);
		if (functionName!=null){
			CallBackFunction f = responseCallbacks.get(functionName);
			String data = BridgeUtil.getDataFromReturnUrl(url);
			if (f != null) {
				f.onCallBack(data);
				responseCallbacks.remove(functionName);
			}
		}
	}



	/**
	 * 保存message到消息队列
	 * @param handlerName handlerName
	 * @param data data
	 * @param responseCallback CallBackFunction
	 */
	private void doSend(String handlerName, String data, CallBackFunction responseCallback) {
		//创建message对象，主要是将js的方法名称，传递的数据，封装到对象中
		WebJsMessage m = new WebJsMessage();
		//判断是否有data数据
		if (!TextUtils.isEmpty(data)) {
			m.setData(data);
		}
		//判断responseCallback是否为null
		if (responseCallback != null) {
			String callbackStr = String.format(BridgeUtil.CALLBACK_ID_FORMAT,
					++uniqueId + (BridgeUtil.UNDERLINE_STR + SystemClock.currentThreadTimeMillis()));
			responseCallbacks.put(callbackStr, responseCallback);
			//m.setCallbackId(callbackStr)方法的作用？
			//该方法设置的callbackId生成后不仅仅会被传到Js，
			//而且会以key-value对的形式和responseCallback配对保存到responseCallbacks这个Map里面。
			//它的目的，就是为了等Js把处理结果回调给Java层后，
			//Java层能根据callbackId找到对应的responseCallback，做后续的回调处理。
			m.setCallbackId(callbackStr);
		}
		//判断是否有handlerName数据
		if (!TextUtils.isEmpty(handlerName)) {
			m.setHandlerName(handlerName);
		}
		//开始分发数据
		queueMessage(m);
	}

	/**
	 * list<message> != null 添加到消息集合否则分发消息
	 * @param m Message
	 */
	private void queueMessage(WebJsMessage m) {
		if (startupMessage != null) {
			startupMessage.add(m);
		} else {
			dispatchMessage(m);
		}
	}

	/**
	 * 分发message 必须在主线程才分发成功
	 * @param m Message
	 */
	public void dispatchMessage(WebJsMessage m) {
		String messageJson = m.toJson();
		//增加非空判断的逻辑
		if (messageJson!=null){
			//escape special characters for json string  为json字符串转义特殊字符
			messageJson = messageJson.replaceAll("(\\\\)([^utrn])", "\\\\\\\\$1$2");
			messageJson = messageJson.replaceAll("(?<=[^\\\\])(\")", "\\\\\"");
			messageJson = messageJson.replaceAll("(?<=[^\\\\])(\')", "\\\\\'");
			messageJson = messageJson.replaceAll("%7B", URLEncoder.encode("%7B"));
			messageJson = messageJson.replaceAll("%7D", URLEncoder.encode("%7D"));
			messageJson = messageJson.replaceAll("%22", URLEncoder.encode("%22"));
			//转化格式为javascript:WebViewJavascriptBridge._handleMessageFromNative('%s');
			String javascriptCommand = String.format(BridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson);
			Message message = handler.obtainMessage();
			message.what = EXEC_SCRIPT;
			message.obj = javascriptCommand;
			handler.sendMessage(message);
			// 必须要找主线程才会将数据传递出去 --- 划重点
			/*if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
				X5LogUtils.d("分发message--------------"+javascriptCommand);
				//this.loadUrl(javascriptCommand);
				//开始执行js中_handleMessageFromNative方法
				if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT &&
						javascriptCommand.length()>=URL_MAX_CHARACTER_NUM) {
					this.evaluateJavascript(javascriptCommand,null);
				}else {
					this.loadUrl(javascriptCommand);
				}
			}*/
		}
	}

	/**
	 * 刷新消息队列
	 */
	public void flushMessageQueue() {
		if (Thread.currentThread() == Looper.getMainLooper().getThread()) {
			loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction() {
				@Override
				public void onCallBack(String data) {
					// deserializeMessage 反序列化消息
					List<WebJsMessage> list = null;
					try {
						list = WebJsMessage.toArrayList(data);
					} catch (Exception e) {
						e.printStackTrace();
						return;
					}
					if (list == null || list.size() == 0) {
						return;
					}
					for (int i = 0; i < list.size(); i++) {
						WebJsMessage m = list.get(i);
						String responseId = m.getResponseId();
						// 是否是response  CallBackFunction
						if (!TextUtils.isEmpty(responseId)) {
							CallBackFunction function = responseCallbacks.get(responseId);
							String responseData = m.getResponseData();
							function.onCallBack(responseData);
							responseCallbacks.remove(responseId);
						} else {
							CallBackFunction responseFunction = null;
							// if had callbackId 如果有回调Id
							final String callbackId = m.getCallbackId();
							if (!TextUtils.isEmpty(callbackId)) {
								responseFunction = new CallBackFunction() {
									@Override
									public void onCallBack(String data) {
										WebJsMessage responseMsg = new WebJsMessage();
										responseMsg.setResponseId(callbackId);
										responseMsg.setResponseData(data);
										queueMessage(responseMsg);
									}
								};
							} else {
								responseFunction = new CallBackFunction() {
									@Override
									public void onCallBack(String data) {
										// do nothing
									}
								};
							}
							// BridgeHandler执行
							BridgeHandler handler;
							if (!TextUtils.isEmpty(m.getHandlerName())) {
								handler = messageHandlers.get(m.getHandlerName());
							} else {
								handler = defaultHandler;
							}
							if (handler != null){
								handler.handler(m.getData(), responseFunction);
							}
						}
					}
				}
			});
		}
	}


	public void loadUrl(String jsUrl, CallBackFunction returnCallback) {
		this.loadUrl(jsUrl);
		// 添加至 Map<String, CallBackFunction>
		responseCallbacks.put(BridgeUtil.parseFunctionName(jsUrl), returnCallback);
	}

	/**
	 * register handler,so that javascript can call it
	 * 注册处理程序,以便javascript调用它
	 * @param handlerName 					handlerName
	 * @param handler 						BridgeHandler
	 */
	public void registerHandler(String handlerName, BridgeHandler handler) {
		if (handler != null) {
			// 添加至 Map<String, BridgeHandler>
			messageHandlers.put(handlerName, handler);
		}
	}

	/**
	 * 解绑注册js操作，一般可以在onDestory中处理
	 * @param handlerName					方法name
	 */
	public void unregisterHandler(String handlerName) {
		if (handlerName != null) {
			messageHandlers.remove(handlerName);
		}
	}

	/**
	 * call javascript registered handler
	 * 调用javascript处理程序注册
	 * @param handlerName handlerName
	 * @param data data
	 * @param callBack CallBackFunction
	 */
	public void callHandler(String handlerName, String data, CallBackFunction callBack) {
		doSend(handlerName, data, callBack);
	}


}
